#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/cpu.h>
#include <linux/sched.h>
#include <linux/sched/task.h>
#include <linux/sched/init.h>
#include <linux/stdlib.h>
#include <linux/irqflags.h>

#include <linux/delay.h>

struct kthread
{
    int (*threadfn)(void *);
    void *data;
};

static long stack_ktd[THREAD_SIZE / sizeof(long)];
static struct task_struct task_ktd = {
    .policy = SCHED_RR,
};

static int kthreadd(void *unused)
{
    while (1)
    {
        msleep(1000);
    }

    return 0;
}

static int kthread_fn(void *_create)
{
    struct kthread *kthread = _create;

    kthread->threadfn(kthread->data);

    return 0;
}

void kthread_daemon_init(void)
{
    //TODO kernel_thread_init(&task_ktd, stack_ktd, kthreadd, NULL, "kthreadd", 0);
}

static struct task_struct *__kthread_create_on_node(int (*threadfn)(void *data),
                                                    void *data, int node,
                                                    const char namefmt[],
                                                    va_list args)
{
    struct task_struct *task = NULL;
    struct kthread *kthread;
    void *stack;
    char *ptr;
    unsigned size;
    char name[16];

    size = THREAD_SIZE + sizeof(struct kthread) + sizeof(struct task_struct);

    ptr = kzalloc(size, GFP_KERNEL);
    if (ptr)
    {
        kthread = (struct kthread *)ptr;
        task = (struct task_struct *)(ptr + sizeof(struct kthread));
        stack = (void *)(task + 1);

        kthread->threadfn = threadfn;
        kthread->data = data;

        vsnprintf(name, sizeof(name), namefmt, args);
  
        task->policy = SCHED_RR;

        kernel_thread_init(task, stack, kthread_fn, kthread, name, 0);
    }

    return task;
}

struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
                                           void *data, int node,
                                           const char namefmt[], ...)
{
    struct task_struct *task;
    va_list args;

    va_start(args, namefmt);
    task = __kthread_create_on_node(threadfn, data, node, namefmt, args);
    va_end(args);

    return task;
}
